package com.haohan.cloud.scm.supply.core.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.haohan.cloud.scm.api.bill.dto.OrderInfoDTO;
import com.haohan.cloud.scm.api.constant.NumberPrefixConstant;
import com.haohan.cloud.scm.api.constant.enums.bill.OrderTypeEnum;
import com.haohan.cloud.scm.api.constant.enums.common.DeliveryTypeEnum;
import com.haohan.cloud.scm.api.constant.enums.common.UseStatusEnum;
import com.haohan.cloud.scm.api.constant.enums.crm.PayStatusEnum;
import com.haohan.cloud.scm.api.constant.enums.saleb.BillTypeEnum;
import com.haohan.cloud.scm.api.constant.enums.saleb.BuyOrderStatusEnum;
import com.haohan.cloud.scm.api.constant.enums.supply.PdsOfferStatusEnum;
import com.haohan.cloud.scm.api.goods.dto.GoodsModelDTO;
import com.haohan.cloud.scm.api.goods.entity.GoodsModel;
import com.haohan.cloud.scm.api.goods.req.GoodsModelFeignReq;
import com.haohan.cloud.scm.api.manage.entity.Merchant;
import com.haohan.cloud.scm.api.opc.entity.ShipRecord;
import com.haohan.cloud.scm.api.saleb.entity.BuyOrderDetail;
import com.haohan.cloud.scm.api.saleb.vo.BuyOrderDetailVO;
import com.haohan.cloud.scm.api.saleb.vo.BuyOrderVO;
import com.haohan.cloud.scm.api.supply.entity.*;
import com.haohan.cloud.scm.api.supply.req.order.SupplyOrderConfirmReq;
import com.haohan.cloud.scm.api.supply.req.order.SupplyOrderQueryReq;
import com.haohan.cloud.scm.api.supply.trans.SupplyOrderTrans;
import com.haohan.cloud.scm.api.supply.vo.*;
import com.haohan.cloud.scm.common.tools.exception.ErrorDataException;
import com.haohan.cloud.scm.common.tools.util.ScmIncrementUtil;
import com.haohan.cloud.scm.supply.core.ScmSupplyOrderCoreService;
import com.haohan.cloud.scm.supply.service.*;
import com.haohan.cloud.scm.supply.utils.ScmSupplyUtils;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author dy
 * @date 2020/4/21
 */
@Service
@AllArgsConstructor
public class ScmSupplyOrderCoreServiceImpl implements ScmSupplyOrderCoreService {

    private final ScmSupplyUtils scmSupplyUtils;
    private final ScmIncrementUtil scmIncrementUtil;
    private final SupplyOrderService supplyOrderService;
    private final SupplyOrderDetailService orderDetailService;
    private final SupplierService supplierService;
    private final OfferOrderService offerOrderService;
    private final SupplierGoodsService supplierGoodsService;
    private final SupplyOrderDetailService supplyOrderDetailService;

    @Override
    public IPage<SupplyOrder> findPage(Page<SupplyOrder> page, SupplyOrderQueryReq req) {
        SupplyOrder query = req.transTo();
        return supplyOrderService.page(page, Wrappers.query(query).lambda()
                .like(StrUtil.isNotEmpty(req.getSupplierName()), SupplyOrder::getSupplierName, req.getSupplierName())
                .like(StrUtil.isNotEmpty(req.getContact()), SupplyOrder::getContact, req.getContact())
                .like(StrUtil.isNotEmpty(req.getTelephone()), SupplyOrder::getTelephone, req.getTelephone())
                .like(StrUtil.isNotEmpty(req.getAddress()), SupplyOrder::getAddress, req.getAddress())
                .orderByDesc(SupplyOrder::getCreateDate)
        );
    }

    private SupplyOrder checkOrder(String supplySn) {
        SupplyOrder order = supplyOrderService.fetchBySn(supplySn);
        if (null == order) {
            throw new ErrorDataException("供应订单有误");
        }
        return order;
    }

    @Override
    public SupplyOrderVO fetchInfo(String supplySn) {
        SupplyOrder order = checkOrder(supplySn);
        List<SupplyOrderDetail> list = orderDetailService.findListBySn(supplySn);
        if (list.isEmpty()) {
            throw new ErrorDataException("供应订单有误: 无明细信息");
        }
        SupplyOrderVO result = new SupplyOrderVO(order);
        if (null == order.getGoodsNum()) {
            result.setGoodsNum(list.size());
        }
        result.setDetailList(list.stream()
                .map(SupplyOrderDetailVO::new)
                .collect(Collectors.toList()));
        return result;
    }

    /**
     * 根据采购单新增供应订单
     *
     * @param buyOrderSn
     * @return 操作成功 返回供应订单编号列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addByBuyOrder(String buyOrderSn) {
        // 选择采购单
        BuyOrderVO buyOrder = scmSupplyUtils.fetchBuyOrderInfo(buyOrderSn);
        if (buyOrder.getStatus() == BuyOrderStatusEnum.cancel) {
            throw new ErrorDataException(StrUtil.format("选择的采购单({})已取消", buyOrderSn));
        }
        // 供应订单下单时间、供货时间默认第二天
        LocalDateTime orderTime = LocalDateTime.now();
        LocalDate supplyDate = LocalDate.now().plusDays(1);
        int size = Math.max(8, buyOrder.getDetailList().size() * 4 / 3 + 1);
        // 供应商家id: offerList
        Map<String, List<OfferOrder>> offerMap = new HashMap<>(size);
        Map<String, Supplier> supplierMap = new HashMap<>(size);
        // 采购单明细不能已进行过报价
        int num = offerOrderService.count(Wrappers.<OfferOrder>query().lambda()
                .in(OfferOrder::getBuyDetailSn, buyOrder.getDetailList().stream().map(BuyOrderDetailVO::getBuyDetailSn).collect(Collectors.toList()))
                // 采购单下单当日
                .apply("ask_price_time >= {0}", buyOrder.getBuyTime().toLocalDate())
        );
        if (num > 0) {
            throw new ErrorDataException("采购单明细已进行过报价操作");
        }
        // 判断一个采购单中是否所有商品都有供应商
        buyOrder.getDetailList().forEach(detail -> {
            List<SupplierGoods> relationList = supplierGoodsService.findListByPlatform(detail.getModelId(), UseStatusEnum.enabled);
            // 默认一个供应商 (商家)
            Supplier supplier = chooseSupplier(relationList);
            supplierMap.put(supplier.getMerchantId(), supplier);
            // 明细商品 创建报价单  （同采购商不合并）
            OfferOrder offerOrder = SupplyOrderTrans.createOfferOrder(detail, supplier);
            offerOrder.setAskPriceTime(orderTime);
            List<OfferOrder> offers = offerMap.computeIfAbsent(supplier.getMerchantId(), k -> new ArrayList<>(size));
            offers.add(offerOrder);
        });
        // 同一供应商的可下单商品 创建供应订单
        List<SupplyOrder> supplyOrderList = new ArrayList<>(Math.max(8, offerMap.size() * 4 / 3 + 1));
        offerMap.forEach((key, value) -> {
            SupplyOrder order = new SupplyOrder();
            // 供应订单属性设置
            order.setOrderTime(orderTime);
            order.setSupplyDate(supplyDate);
            Supplier supplier = supplierMap.get(key);
            order.setSupplierId(supplier.getId());
            order.setSupplierName(supplier.getSupplierName());
            supplyOrderList.add(addOrderByOffer(order, value, true));
        });
        String supplySns = supplyOrderList.stream()
                .map(SupplyOrder::getSupplySn)
                .collect(Collectors.joining(StrUtil.COMMA));
        // 创建供应订单后修改采购单状态 为待发货
        scmSupplyUtils.updateBuyOrderStatus(buyOrderSn, BuyOrderStatusEnum.delivery);
        return supplySns;
    }

    private Supplier chooseSupplier(List<SupplierGoods> list) {
        if (CollUtil.isEmpty(list)) {
            throw new ErrorDataException("采购单明细中有商品未关联供应商");
        }
        // 默认选一个关联的供应商家 的启用供应商
        return supplierService.list(Wrappers.<Supplier>query().lambda()
                .in(Supplier::getMerchantId, list.stream().map(SupplierGoods::getSupplierMerchantId).collect(Collectors.toList()))
                .eq(Supplier::getStatus, UseStatusEnum.enabled)
        ).stream()
                .findFirst().orElseThrow(() -> new ErrorDataException("无启用的关联供应商"));
    }

    /**
     * 根据报价单 新增供应订单
     *
     * @param supplyOrder
     * @param offerOrderList
     * @param isAdd
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SupplyOrder addOrderByOffer(SupplyOrder supplyOrder, List<OfferOrder> offerOrderList, boolean isAdd) {
        // 供应商验证
        Supplier supplier = supplierService.getById(supplyOrder.getSupplierId());
        if (null == supplier) {
            throw new ErrorDataException("供应商有误");
        }
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal otherAmount = BigDecimal.ZERO;
        for (OfferOrder offer : offerOrderList) {
            if (!checkOffer(supplier.getId(), offer)) {
                throw new ErrorDataException("报价单有误");
            }
            BigDecimal amount = offer.getBuyNum().multiply(offer.getDealPrice());
            BigDecimal other = offer.getOtherAmount() == null ? BigDecimal.ZERO : offer.getOtherAmount();
            offer.setTotalAmount(amount.add(other));
            offer.setOtherAmount(other);
            totalAmount = totalAmount.add(amount);
            otherAmount = otherAmount.add(other);
        }
        String sn = scmIncrementUtil.inrcSnByClass(SupplyOrder.class, NumberPrefixConstant.SUPPLY_ORDER_SN_PRE);
        supplyOrder.setId(null);
        supplyOrder.setSupplySn(sn);
        SupplyOrderTrans.copySupplier(supplyOrder, supplier);
        supplyOrder.setTotalAmount(totalAmount);
        supplyOrder.setOtherAmount(otherAmount);
        supplyOrder.setSumAmount(totalAmount.subtract(otherAmount));
        supplyOrder.setStatus(BuyOrderStatusEnum.wait);
        // todo 其余属性
        supplyOrder.setDeliveryType(DeliveryTypeEnum.express);
        supplyOrder.setGoodsNum(offerOrderList.size());
        supplyOrderService.save(supplyOrder);
        List<GoodsModel> storageList = new ArrayList<>(offerOrderList.size());
        offerOrderList.forEach(offer -> {
            // 供应商品关联
            SupplierGoods relation = supplierGoodsService.fetchOneByModel(supplier.getMerchantId(), null, offer.getGoodsModelId());
            if (null == relation) {
                throw new ErrorDataException("查询平台商品规格对应的供应商品规格有误");
            }
            GoodsModelFeignReq feignReq = new GoodsModelFeignReq();
            feignReq.setMerchantId(supplier.getMerchantId());
            feignReq.setModelId(relation.getSupplyModelId());
            GoodsModelDTO supplyModel = scmSupplyUtils.fetchGoodsModelDTO(feignReq);
            if (null == supplyModel) {
                throw new ErrorDataException("供应商品规格有误");
            }
            // 保存供应订单明细
            SupplyOrderDetail detail = SupplyOrderTrans.initDetailByOffer(offer, supplyModel);
            detail.setSupplySn(sn);
            supplyOrderDetailService.save(detail);
            offer.setSupplySn(sn);
            offer.setSupplyDetailSn(detail.getSupplyDetailSn());
            if (isAdd) {
                offerOrderService.save(offer);
            } else {
                offerOrderService.updateById(offer);
            }
            GoodsModel model = new GoodsModel();
            model.setId(supplyModel.getId());
            model.setModelStorage(detail.getGoodsNum());
            storageList.add(model);
        });
        // 供应商品规格库存扣减
        scmSupplyUtils.modelStorageSubtract(storageList);
        return supplyOrder;
    }

    /**
     * 验证报价单
     *
     * @param supplierId
     * @param offer
     * @return
     */
    private boolean checkOffer(String supplierId, OfferOrder offer) {
        if (!StrUtil.equals(supplierId, offer.getSupplierId())) {
            return false;
        }
        // todo 其他状态验证
        if (offer.getStatus() == PdsOfferStatusEnum.notBidding) {
            return false;
        }
        return true;
    }

    /**
     * 供应订单确认
     * 状态 -> 待发货
     * 会创建发货单
     *
     * @param req
     * @return
     */
    @Override
    public boolean orderConfirm(SupplyOrderConfirmReq req) {
        SupplyOrder order = checkOrder(req.getSupplySn());
        if (order.getStatus() != BuyOrderStatusEnum.wait) {
            throw new ErrorDataException("供应订单状态有误: 不为待确认");
        }
        // 创建账单(advanceAmount=0 不需预付) 账单为平台与供应商结算
        scmSupplyUtils.createPayableBill(req.getSupplySn(), BigDecimal.ZERO);
        // 创建发货记录
        scmSupplyUtils.createShipRecord(req.getSupplySn());

        SupplyOrder update = new SupplyOrder();
        update.setId(order.getId());
        update.setStatus(BuyOrderStatusEnum.delivery);
        update.setDealTime(LocalDateTime.now());
        update.setRemarks(req.getRemarks());
        return supplyOrderService.updateById(update);
    }

    /**
     * 删除供应订单
     *
     * @param id
     * @return
     */
    @Override
    public boolean deleteOrder(String id) {
        SupplyOrder order = supplyOrderService.getById(id);
        if (null == order) {
            throw new ErrorDataException("删除供应订单");
        }
        if (order.getStatus() != BuyOrderStatusEnum.cancel && order.getStatus() != BuyOrderStatusEnum.submit
                && order.getStatus() != BuyOrderStatusEnum.wait) {
            throw new ErrorDataException("订单当前状态, 不可删除");
        }
        // 报价单
        int num = offerOrderService.count(Wrappers.<OfferOrder>query().lambda()
                .eq(OfferOrder::getSupplySn, order.getSupplySn())
        );
        if (num > 0) {
            throw new ErrorDataException("订单存在报价单, 不可删除");
        }
        // 删除账单、发货单
        if (!scmSupplyUtils.deleteShipRecord(order.getSupplySn(), OrderTypeEnum.supply)) {
            throw new ErrorDataException("已发货订单不可删除");
        }
        if (!scmSupplyUtils.deleteBillByOrder(order.getSupplySn(), BillTypeEnum.purchase)) {
            throw new ErrorDataException("有已审核通过账单, 不可删除订单");
        }
        List<SupplyOrderDetail> detailList = supplyOrderDetailService.findListBySn(order.getSupplySn());
        if (CollUtil.isNotEmpty(detailList)) {
            supplyOrderDetailService.removeByIds(detailList.stream()
                    .map(SupplyOrderDetail::getId).collect(Collectors.toList()));
        }
        return supplyOrderService.removeById(order.getId());
    }

    /**
     * 供应订单信息 （对应商品为供应商家的）
     *
     * @param supplySn
     * @return
     */
    @Override
    public OrderInfoDTO fetchOrderInfoBySupply(String supplySn) {
        SupplyOrder order = checkOrder(supplySn);
        List<SupplyOrderDetail> detailList = supplyOrderDetailService.findListBySn(supplySn);
        if (CollUtil.isEmpty(detailList)) {
            throw new ErrorDataException("订单明细有误");
        }
        OrderInfoDTO info = SupplyOrderTrans.transToOrderInfoBySupply(order, detailList);
        // pmId、pmName、merchantId、merchantNam、customerId、customerName信息
        Supplier supplier = supplierService.getById(order.getSupplierId());
        if (null == supplier) {
            throw new ErrorDataException("供应商有误");
        }
        Merchant platformMerchant = scmSupplyUtils.fetchPlatformMerchant();
        info.setPmId(platformMerchant.getId());
        info.setPmName(platformMerchant.getMerchantName());
        info.setMerchantId(supplier.getMerchantId());
        info.setMerchantName(supplier.getMerchantName());
        info.setCustomerId(supplier.getId());
        info.setCustomerName(supplier.getSupplierName());
        // 支付状态
        info.setPayStatus(scmSupplyUtils.queryOrderPayStatus(supplySn));
        return info;
    }

    /**
     * 查询B客户采购的关联供应订单信息
     * 供应订单编号, 支付状态
     *
     * @param buyOrderSn
     * @return
     */
    @Override
    public SupplyRelationBuyOrderVO buyOrderRelation(String buyOrderSn) {
        // 采购订单查询
        BuyOrderVO buyOrder = scmSupplyUtils.fetchBuyOrderInfo(buyOrderSn);
        // 中标的报价单
        List<OfferOrder> offerOrderList = offerOrderService.findListByBuyDetailSn(buyOrder.getDetailList().stream()
                .map(BuyOrderDetailVO::getBuyDetailSn).collect(Collectors.toList()));
        // 通过采购订单查询支付状态
        PayStatusEnum payStatus = buyOrder.getPayStatus();
        if (offerOrderList.isEmpty()) {
            return SupplyOrderTrans.initSupplyRelationBuyOrder(payStatus);
        }
        SupplyRelationBuyOrderVO result = new SupplyRelationBuyOrderVO();
        result.setPayStatus(payStatus);
        result.setStatus(true);

        int size = Math.max(8, offerOrderList.size() * 4 / 3 + 1);
        Set<String> supplySnSet = new HashSet<>(size);
        List<SupplyRelationBuyOrderDetailVO> detailList = new ArrayList<>(size);
        offerOrderList.forEach(offer -> {
            supplySnSet.add(offer.getSupplySn());
            detailList.add(new SupplyRelationBuyOrderDetailVO(buyOrderSn, offer.getBuyDetailSn(), offer.getSupplySn(), offer.getSupplyDetailSn()));
        });
        result.setDetailList(detailList);
        result.setSupplySns(CollUtil.join(supplySnSet, StrUtil.COMMA));
        return result;
    }

    /**
     * 查询供应订单对应采购单信息
     * 采购单编号, 发货单编号
     *
     * @param supplySn
     * @return
     */
    @Override
    public SupplyBuyOrderInfoVO relationBuyInfo(String supplySn) {
        checkOrder(supplySn);
        // 中标的报价单
        List<OfferOrder> offerOrderList = offerOrderService.list(Wrappers.<OfferOrder>query().lambda()
                .eq(OfferOrder::getStatus, PdsOfferStatusEnum.bidding)
                .eq(OfferOrder::getSupplySn, supplySn)
        );
        // 发货记录
        ShipRecord shipRecord = scmSupplyUtils.fetchShipRecord(supplySn);
        if (offerOrderList.isEmpty()) {
            return SupplyOrderTrans.initSupplyBuyOrderInfo(shipRecord);
        }
        int size = Math.max(8, offerOrderList.size() * 4 / 3 + 1);
        Set<String> buyOrderSnSet = new HashSet<>(size);
        List<SupplyRelationBuyOrderDetailVO> detailList = new ArrayList<>(size);
        offerOrderList.forEach(offer -> {
            BuyOrderDetail buyDetail = scmSupplyUtils.fetchBuyOrderDetail(offer.getBuyDetailSn());
            buyOrderSnSet.add(buyDetail.getBuyId());
            detailList.add(new SupplyRelationBuyOrderDetailVO(buyDetail.getBuyId(), offer.getBuyDetailSn(), offer.getSupplySn(), offer.getSupplyDetailSn()));
        });
        SupplyBuyOrderInfoVO result = new SupplyBuyOrderInfoVO();
        result.setDetailList(detailList);
        result.setBuyOrderSns(CollUtil.join(buyOrderSnSet, StrUtil.COMMA));
        result.setShipRecordSn(null == shipRecord ? "" : shipRecord.getShipRecordSn());
        return result;
    }
}
